<?php

namespace App\Kuafu\Wechat\Service;

use App\Kuafu\Shop\Service\ShopAppletService;
use App\Kuafu\Wechat\Enum\CodeEnum;
use App\Kuafu\Wechat\Enum\WechatCodeEnum;
use App\Kuafu\Wechat\Exceptions\WechatException;
use App\Library\FeiShu\SendFs;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
use App\Kuafu\Base\Service\BaseService;
use GuzzleHttp\Client as GuzzleClient;
use App\Kuafu\Order\Enum\CodeEnum as OrderCodeEnum;

class WechatService extends BaseService
{

    /**
     * 登录凭证校验
     * @param $code
     * @param string $appid
     * @return array
     * @throws WechatException
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public static function AuthCodeToSession($code, string $appid): array
    {
        Log::channel('wechat')->info('用户授权微信请求数据：' . json_encode(['appId' => $appid]));
        $client     = new GuzzleClient();
        $appletInfo = ShopAppletService::getAppletInfoByAppId($appid);
        $appid      = $appletInfo['appId'];
        $secret     = $appletInfo['appSecret'];
        $jsCode     = $code;
        $grandType  = 'authorization_code';
        Log::channel('wechat')->info('用户授权微信请求数据：' . json_encode([
                'appId'  => $appid,
                'secret' => $secret,
                'jsCode' => $jsCode
            ]));
        $url = "https://api.weixin.qq.com/sns/jscode2session?appid=$appid&secret=$secret&js_code=$jsCode&grant_type=$grandType";
        try {
            $response = $client->get($url);
            $result   = $response->getBody()->getContents();
            Log::channel('wechat')->info('用户授权微信返回数据：' . $result);
            $responseArr = json_decode($result, true);
            if (isset($responseArr['errcode']) && $responseArr['errcode'] != 0) {
                throw new WechatException(WechatCodeEnum::APPID_ERROR);
            }
            return $responseArr;
        } catch (\Exception $exception) {
            throw new WechatException(WechatCodeEnum::ERR_SERVER_ERROR, $exception->getMessage());
        }

    }

    /**
     * 获取token
     * @return mixed
     * @throws WechatException
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public static function getAccessToken($shopId = 0)
    {
        //查询accessToken缓存
        $appletInfo = ShopAppletService::getAppletInfo($shopId);
        Log::channel('wechat')->info("获取token数据来源--{$shopId}" . json_encode($appletInfo));
        $accessTokenCache = Redis::get(CodeEnum::PREFIX_ACCESS_TOKEN . $appletInfo['appId']);
        if (!$accessTokenCache) {
            $grantType   = CodeEnum::GRANT_TYPE;
            $appid       = $appletInfo['appId'];
            $secret      = $appletInfo['appSecret'];
            $client      = new GuzzleClient();
            $url         = CodeEnum::GET_TOKEN_URL . "$grantType&appid=$appid&secret=$secret";
            $response    = $client->get($url);
            $result      = $response->getBody()->getContents();
            $responseArr = json_decode($result, true);
            if (isset($responseArr['errcode']) && $responseArr['errcode'] != 0) {
                throw new WechatException(WechatCodeEnum::APPID_ERROR);
            }
            Redis::setex(CodeEnum::PREFIX_ACCESS_TOKEN . $appletInfo['appId'], 7000, $responseArr['access_token']);
            return $responseArr['access_token'];
        } else {
            return $accessTokenCache;
        }
    }

    /**
     * 发送模板消息
     * @param $url string 消息url
     * @param $data array 发送数据
     * @return mixed
     * @throws WechatException
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public static function sendMessage($url, $data, $title = '发送消息模板')
    {
        $client      = new GuzzleClient();
        $body        = json_encode($data);
        $response    = $client->request("POST", $url, ['body' => $body, 'timeout' => 10]);
        $response    = $response->getBody()->getContents();
        $responseArr = json_decode($response, true);
        Log::channel('wechat')->info($title . '调用返回：', $responseArr);
        return $responseArr;
    }

    /**
     * 解密
     * @param $encryptedData
     * @param $iv
     * @param $sessionKey
     * @param int $shopId
     * @return false|string
     * @throws WechatException
     */
    public static function decryptData($encryptedData, $iv, $sessionKey, int $shopId)
    {
        $appletInfo = ShopAppletService::getAppletInfo($shopId);
        $data       = ['data' => $encryptedData, 'iv' => $iv, 'key' => $sessionKey, 'appid' => $appletInfo['appId']];
        Log::channel('wechat')->info('解密原始数据:' . json_encode($data));
        if (strlen($sessionKey) != 24) {
            throw new WechatException(WechatCodeEnum::ILLEGAL_AES_KRY);
        }
        $aesKey = base64_decode($sessionKey);
        if (strlen($iv) != 24) {
            throw new WechatException(WechatCodeEnum::ILLEGAL_IV);
        }
        $aesIV     = base64_decode($iv);
        $aesCipher = base64_decode($encryptedData);
        $result    = openssl_decrypt($aesCipher, "AES-128-CBC", $aesKey, 1, $aesIV);
        $dataObj   = json_decode($result);
        if ($dataObj == null) {
            Log::channel('wechat')->error("解密错误信息：" . openssl_error_string());
            throw new WechatException(WechatCodeEnum::ILLEGAL_BUFFER);
        }
        if ($dataObj->watermark->appid != $appletInfo['appId']) {
            throw new WechatException(WechatCodeEnum::ILLEGAL_BUFFER_APPID);
        }
        return $result;
    }

    /**
     * 获取模板列表
     * @param int $type
     * @param int $shopId
     * @return mixed
     */
    public static function getTemplateList(int $type, int $shopId)
    {
        $data         = [];
        $templateInfo = ShopAppletService::getAppletTemplateInfo($shopId);
        if ($type == 1) {
            $data[0]['title']     = '取餐提醒';
            $data[0]['priTmplId'] = $templateInfo['callNum'];
            $data[1]['title']     = '优惠券到账通知';
            $data[1]['priTmplId'] = $templateInfo['arrival'];
            $data[2]['title']     = '优惠券过期提醒';
            $data[2]['priTmplId'] = $templateInfo['expire'];
        } elseif ($type == 2) {
            $data[0]['title']     = '优惠券到账通知';
            $data[0]['priTmplId'] = $templateInfo['arrival'];
            $data[1]['title']     = '优惠券过期提醒';
            $data[1]['priTmplId'] = $templateInfo['expire'];
        }
        return $data;
    }

    /**
     * 生成小程序二维码
     * @param $type 1:二维码 2 菊花码
     * @param $data  array("path" => $path, "width" => 150)
     * @return string
     * @throws WechatException
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public static function createQrCode($type, $data, int $shopId = 0)
    {
        $url      = OrderCodeEnum::QR_CODE_TEXT[$type];
        $url      = "$url?access_token=" . self::getAccessToken($shopId);
        $body     = json_encode($data);
        $client   = new GuzzleClient();
        $response = $client->request("POST", $url, ['body' => $body, 'timeout' => 10]);
        $response = $response->getBody()->getContents();
        //生成二维码
        return "data:image/jpeg;base64," . base64_encode($response);
    }


    /**
     * 获取微信小程序 Short Link
     * @param $shopId
     * @param string $linkUrl
     * @param string $title
     * @param bool $is_permanent
     * @return mixed
     * @throws GuzzleException
     * @throws WechatException
     */
    public static function genWxShortLink($shopId, string $linkUrl, string $title, $is_permanent = false)
    {
        //获取access_token
        $token = self::getAccessToken($shopId);
        $url = "https://api.weixin.qq.com/wxa/genwxashortlink?access_token=$token";

        $data = [
            'page_url' => $linkUrl,//通过 Short Link 进入的小程序页面路径，必须是已经发布的小程序存在的页面，可携带 query，最大1024个字符
            'page_title' => $title,//页面标题，不能包含违法信息，超过20字符会用... 截断代替
            'is_permanent' => $is_permanent,//生成的 Short Link 类型，短期有效：false，永久有效：true
        ];
        $body = json_encode($data, JSON_UNESCAPED_UNICODE);
        $client = new GuzzleClient();
        $response = $client->request("POST", $url, ['body' => $body, 'timeout' => 10]);
        $response = $response->getBody()->getContents();
        $responseArr = json_decode($response, true);
        Log::channel('wechat')->info("获取微信小程序短链接返回", $responseArr);
        if (isset($responseArr['errcode']) && $responseArr['errcode'] == 0) {
            return $responseArr['link'];// 成功返回短链接
        }
        return '';
    }

    /**
     * 微信服务号-登录凭证校验
     * @param $code
     * @param string $appid
     * @param int $platformId
     * @return array
     * @throws GuzzleException
     * @throws WechatException
     */
    public static function AuthCodeToAccessToken($code, string $appid, int $platformId): array
    {
        Log::channel('wechat')->info('用户授权微信服务号请求数据：' . json_encode(['appId' => $appid, 'platformId' => $platformId]));
        $client    = new GuzzleClient();
        $appid     = config('wechat.publicAppid');
        $secret    = config('wechat.publicSecret');
        $jsCode    = $code;
        $grandType = 'authorization_code';
        Log::channel('wechat')->info('用户授权微信服务号请求数据：' . json_encode([
                'appId'      => $appid,
                'secret'     => $secret,
                'jsCode'     => $jsCode,
                'platformId' => $platformId,
            ]));
        $url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=$appid&secret=$secret&code=$jsCode&grant_type=$grandType";
        try {
            $response = $client->get($url);
            $result   = $response->getBody()->getContents();
            Log::channel('wechat')->info('用户授权微信服务号返回数据：' . $result);
            $responseArr = json_decode($result, true);
            if (isset($responseArr['errcode']) && $responseArr['errcode'] != 0) {
                throw new WechatException(WechatCodeEnum::APPID_ERROR);
            }

            // 拉取用户信息
            $userInfo                  = self::getUserinfo($responseArr['access_token'], $responseArr['openid']);
            $responseArr['nickname']   = $userInfo['nickname'] ?? '';
            $responseArr['headimgurl'] = $userInfo['headimgurl'] ?? '';

            return $responseArr;
        } catch (\Exception $exception) {
            throw new WechatException(WechatCodeEnum::ERR_SERVER_ERROR, $exception->getMessage());
        }

    }

    /**
     * 微信服务号-拉取用户信息
     * @param string $accessToken
     * @param string $openid
     * @return array
     * @throws GuzzleException
     * @throws WechatException
     */
    public static function getUserinfo(string $accessToken, string $openid): array
    {
        Log::channel('wechat')->info('微信服务号拉取用户信息请求数据：' . json_encode([
                'accessToken' => $accessToken,
                'openid'      => $openid
            ]));

        $client = new GuzzleClient();

        $url = "https://api.weixin.qq.com/sns/userinfo?access_token=$accessToken&openid=$openid&lang=zh_CN";
        try {
            $response = $client->get($url);
            $result   = $response->getBody()->getContents();
            Log::channel('wechat')->info('微信服务号拉取用户信息返回数据：' . $result);
            $responseArr = json_decode($result, true);
            if (isset($responseArr['errcode']) && $responseArr['errcode'] != 0) {
                throw new WechatException(WechatCodeEnum::APPID_ERROR);
            }
            return $responseArr;
        } catch (\Exception $exception) {
            throw new WechatException(WechatCodeEnum::ERR_SERVER_ERROR, $exception->getMessage());
        }
    }
}
